Skip to content

a11y(3.3.1): Holocene input primitives — add aria-invalid, aria-describedby, and uniform live-region error announcement#3554

Open
rosanusi wants to merge 1 commit into
mainfrom
wcag/3.3.1-input-primitives-error-association
Open

a11y(3.3.1): Holocene input primitives — add aria-invalid, aria-describedby, and uniform live-region error announcement#3554
rosanusi wants to merge 1 commit into
mainfrom
wcag/3.3.1-input-primitives-error-association

Conversation

@rosanusi

@rosanusi rosanusi commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds uniform error-association to all seven Holocene input primitives (Input, Textarea, NumberInput, ChipInput, Combobox, Select, Checkbox).

Each primitive now:

  • Sets aria-invalid="true" on the underlying control when an error is active
  • Generates a unique errorId (${id}-error) per instance and sets aria-describedby pointing at the error element (only when an error is present — no dangling references)
  • Renders the error text element with id={errorId} and role="alert" for consistent live-region announcement

This ensures screen-reader users hear the error text both on first render (live region) and when focus returns to an invalid field (via aria-describedby association).

Before: AT reads only the field label when focus returns to an invalid field; user cannot detect the error state programmatically.
After: AT announces field label + "invalid entry, [error text]" via aria-invalid + aria-describedby.

Files changed

  • src/lib/holocene/input/input.svelte
  • src/lib/holocene/textarea.svelte
  • src/lib/holocene/input/number-input.svelte
  • src/lib/holocene/input/chip-input.svelte
  • src/lib/holocene/combobox/combobox.svelte
  • src/lib/holocene/select/select.svelte
  • src/lib/holocene/checkbox.svelte

Test plan

  • Inspect DOM for each primitive in an erroring state: confirm aria-invalid="true" on the control, id="{id}-error" on the error element, aria-describedby="{id}-error" on the control, and role="alert" on the error element
  • Verify no aria-describedby is set when no error is present (no dangling references)
  • With VoiceOver/NVDA: confirm error is announced on first render and re-announced when focus returns to the invalid field
  • Regression: error text continues to render visually in the same position; non-error hintText still displays correctly when error=false/valid=true
  • axe-core: zero new violations on a representative form page

References

  • WCAG 2.2 SC 3.3.1 Error Identification (Level A)
  • Cross-walks: SC 1.3.1 Info and Relationships, SC 4.1.2 Name, Role, Value
  • A11y-Audit-Ref: 3.3.1-holocene-input-primitives-error-association

🤖 Generated with Claude Code

A11y-Audit-Ref: 3.3.1-holocene-input-primitives-error-association

…l seven Holocene input primitives

Adds uniform error-association across Input, Textarea, NumberInput, ChipInput,
Combobox, Select, and Checkbox so screen-reader users hear the error text when
focus returns to an invalid field.

Each primitive now:
- Sets aria-invalid="true" on the underlying control when an error is active
- Generates a unique errorId from the consumer-supplied id prop and sets
  aria-describedby pointing at the error element (only when an error is present)
- Renders the error text element with id={errorId} and role="alert" for
  consistent live-region announcement across all seven primitives

A11y-Audit-Ref: 3.3.1-holocene-input-primitives-error-association

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@vercel

vercel Bot commented Jun 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
holocene Ready Ready Preview, Comment Jun 12, 2026 1:54pm

Request Review

@github-actions github-actions Bot added a11y Accessibility audit PR a11y:no-fix-doc No A11y-Audit-Ref line; audit team triage labels Jun 12, 2026
@@ -123,6 +123,9 @@
// After all the Options are mounted use context to read the label assocaited with the value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • ⚠️ Argument of type 'T | undefined' is not assignable to parameter of type 'T'.

@temporal-cicd

temporal-cicd Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor
Warnings
⚠️

📊 Strict Mode: 23 errors in 7 files (2.5% of 904 total)

src/lib/holocene/combobox/combobox.svelte (10)
  • L154:4: Type 'null' is not assignable to type 'string'.
  • L157:4: Type 'null' is not assignable to type '"search" | "link" | "success" | "error" | "action" | "activity" | "add-square" | "add" | "apple" | "archives" | "arrow-down" | "arrow-left" | "arrow-up" | "arrow-right" | "ascending" | ... 141 more ... | "xmark-square"'.
  • L159:4: Type 'null' is not assignable to type 'keyof T'.
  • L244:4: 'inputElement' is possibly 'null'.
  • L245:4: 'inputElement' is possibly 'null'.
  • L251:14: Argument of type 'string | T | undefined' is not assignable to parameter of type 'string | T'.
  • L256:14: Argument of type 'string | T | undefined' is not assignable to parameter of type 'string | T'.
  • L300:60: Function lacks ending return statement and return type does not include 'undefined'.
  • L393:10: Type 'HTMLLIElement | null' is not assignable to type 'HTMLLIElement'.
  • L393:43: 'menuElement' is possibly 'null'.
src/lib/holocene/input/input.svelte (2)
  • L48:4: Type 'null' is not assignable to type '"search" | "link" | "success" | "error" | "action" | "activity" | "add-square" | "add" | "apple" | "archives" | "arrow-down" | "arrow-left" | "arrow-up" | "arrow-right" | "ascending" | ... 141 more ... | "xmark-square"'.
  • L103:27: Type 'boolean | null' is not assignable to type 'boolean | undefined'.
src/lib/holocene/textarea.svelte (1)
  • L29:13: Type 'null' is not assignable to type 'boolean'.
src/lib/holocene/checkbox.svelte (3)
  • L33:13: Type 'undefined' is not assignable to type 'T'.
  • L34:13: Type 'undefined' is not assignable to type 'T[]'.
  • L13:12: Argument of type '$$Props' is not assignable to parameter of type '{ id?: string | undefined; checked?: boolean | undefined; label?: string | undefined; labelHidden?: boolean | undefined; indeterminate?: boolean | undefined; disabled?: boolean | undefined; ... 5 more ...; class?: string | undefined; }'.
src/lib/holocene/select/select.svelte (3)
  • L71:4: Type 'null' is not assignable to type '"search" | "link" | "success" | "error" | "action" | "activity" | "add-square" | "add" | "apple" | "archives" | "arrow-down" | "arrow-left" | "arrow-up" | "arrow-right" | "ascending" | ... 141 more ... | "xmark-square"'.
  • L91:30: Argument of type 'T | undefined' is not assignable to parameter of type 'T'.
  • L123:36: Argument of type 'T | undefined' is not assignable to parameter of type 'T'.
src/lib/holocene/input/number-input.svelte (3)
  • L9:13: Type 'null' is not assignable to type '"search" | "link" | "success" | "error" | "action" | "activity" | "add-square" | "add" | "apple" | "archives" | "arrow-down" | "arrow-left" | "arrow-up" | "arrow-right" | "ascending" | ... 141 more ... | "xmark-square"'.
  • L20:13: Type 'undefined' is not assignable to type 'number'.
  • L21:13: Type 'undefined' is not assignable to type 'number'.
src/lib/holocene/input/chip-input.svelte (1)
  • L74:30: 'e.clipboardData' is possibly 'null'.

Generated by 🚫 dangerJS against 27c0913

@rosanusi rosanusi marked this pull request as ready for review June 12, 2026 14:20
@rosanusi rosanusi requested a review from a team as a code owner June 12, 2026 14:20
@github-actions github-actions Bot added a11y:bucket-3 Bucket 3: engineer required a11y:sc-3.3.1 and removed a11y:no-fix-doc No A11y-Audit-Ref line; audit team triage labels Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a11y:bucket-3 Bucket 3: engineer required a11y:sc-3.3.1 a11y Accessibility audit PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant